#pragma once
#include <queue>
#include "base/Task.h"
#include "MessagePump.h"
#include "ThreadDef.h"

namespace xBase
{
    class IxDestructionObserver 
    {
    public:
        virtual ~IxDestructionObserver() {}

        virtual VOID WillDestroyCurrentMessageLoop() = 0;
    };

    class IxIdleHandler
    {
    public:
        virtual BOOL OnIdle() = 0;
    };

    class CxMessageLoop : public IxDelegate
    {
    public:

        friend class CxMessageLoopProxy;

        VOID AddDestructionObserver(IxDestructionObserver* pObserver);
        VOID RemoveDestructionObserver(IxDestructionObserver* pObserver);

        BOOL AddIdleHandler(IxIdleHandler* pHandler);
        BOOL RemoveIdleHandler(IxIdleHandler* pHandler);
        VOID DoIdleHandler();

    public:

        explicit CxMessageLoop(LOOP_TYPE eType = LOOP_TYPE_DEFAULT, const std::string& strThreadName = "TYPE_DEFAULT");

        virtual ~CxMessageLoop(VOID);

        void OnCrash()
        {
            m_pState = NULL;
        }

        BOOL GetMessageLoopProxy(xBase::CScopedRefPtr<CxMessageLoopProxy>& pMsgLoopProxy);

        // post task functions, call PostTask_Helper
        VOID PostTask(CTask * pTask);
        VOID PostDelayedTask(CTask * pTask, INT64 nDelayMilliSecond);
        VOID PostNonNestableTask(CTask * pTask);
        VOID PostNonNestableDelayedTask(CTask * pTask, INT64 nDelayMilliSecond);

        VOID Run();

        VOID RunAllPending();

        VOID Quit();

        VOID QuitNow();

        VOID QuitAll();

        LOOP_TYPE GetType() const {return m_eType;}

        static CxMessageLoop* Current();

        VOID SetNestableTasksAllowed(BOOL bAllowed);
        BOOL NestableTasksAllowed() const;

        BOOL IsNested();

        INT RunDepth();

        INT WaitingTaskNumber();

        VOID RunWithDispatcher(IxDispatcher* pDispatcher)
        {
            CxAutoRunState SaveState(this);
            m_pState->m_pDispatcher = pDispatcher;
            RunHandler();
        }

    protected:
        struct RunState 
        {
            // Used to count how many Run() invocations are on the stack.
            INT m_nRunDepth;

            // Used to record that Quit() was called, or that we should quit the pump
            // once it becomes idle.
            BOOL m_bQuitReceived;

            IxDispatcher * m_pDispatcher;
        };

        // used for state save and restore
        class CxAutoRunState : RunState 
        {
        public:
            explicit CxAutoRunState(CxMessageLoop * pMessageLoop);
            ~CxAutoRunState();

        private:
            CxMessageLoop*  m_pMessageLoop;
            RunState*       m_pPreviousState;
        };

        // This structure is copied around by value.
        struct PendingTask 
        {
            CTask * m_pTask;                    // The task to run.
            CTime   m_DelayedRunTime;           // The time when the task should be run.
            INT     m_nSequenceNum;             // Used to facilitate sorting by run time.
            BOOL    m_bNestable;                // True if OK to dispatch from a nested loop.

            PendingTask(CTask * pTask, BOOL bNestable)
                : m_pTask(pTask), m_nSequenceNum(0), m_bNestable(bNestable)
            {
            }

            // Used to support sorting.
            bool operator<(const PendingTask& other) const;
        };

        class CxTaskQueue : public std::queue<PendingTask> 
        {
        public:
            VOID Swap(CxTaskQueue * pQueue) 
            {
                c.swap(pQueue->c);  // Calls std::deque::swap
            }
        };

        typedef std::priority_queue<PendingTask> DelayedTaskQueue;

        CxMessagePump * Pump() 
        {
            return static_cast<CxMessagePump*>(m_Pump.get());
        }

        VOID RunHandler();

        VOID RunInternal();

        // Called to process any delayed non-nestable tasks : m_DeferredNonNestableWorkQueue.top();
        BOOL ProcessNextDelayedNonNestableTask();

        // Runs the specified task and deletes it.
        VOID RunTask(CTask * pTask);

        // Calls RunTask or queues the pending_task on the deferred task list if it
        // cannot be run right now.  Returns true if the task was run.
        BOOL DeferOrRunPendingTask(const PendingTask& oPendingTask);

        // Adds the pending task to delayed_work_queue_.
        VOID AddToDelayedWorkQueue(const PendingTask& oPendingTask);

        // Load tasks from the incoming_queue_ into work_queue_ if the latter is empty.
        VOID ReloadWorkQueue();

        // Delete tasks that haven't run yet without running them.  Used in the
        // destructor to make sure all the task's destructors get called.  Returns
        // true if some work was done.
        BOOL DeletePendingTasks();

        // Post a task to our incomming queue.
        VOID PostTask_Helper(CTask * pTask,	INT64 nDelayMilliSecond, BOOL bNestable);

        // xBase::CxMessagePump::Delegate methods:
        virtual BOOL DoWork();
        virtual BOOL DoDelayedWork(CTime * pNextDelayedWorkTime);
        virtual BOOL DoIdleWork();

    protected:
        LOOP_TYPE           m_eType;
        std::string         m_strThreadName;

        CxTaskQueue         m_IncomingQueue;
        CxLock              m_IncomingQueueLock;

        CxTaskQueue         m_WorkQueue;

        DelayedTaskQueue    m_DelayedWorkQueue;

        CxTaskQueue         m_DeferredNonNestableWorkQueue;

        CScopedRefPtr<CxMessagePump> m_Pump;

        BOOL                m_bNestableTasksAllowed;

        RunState*           m_pState;

        INT                 m_nNextSequenceNum;

        CxLock                              m_DestructionObserversListLock;
        std::list<IxDestructionObserver*>   m_listDestructionObservers;
        std::list<IxIdleHandler*>           m_listIdleHandler;

        CScopedRefPtr<CxMessageLoopProxy>   m_msgLoopProxy;
        BOOL                                m_bQuitAll;

    private:
        CxMessageLoop(const CxMessageLoop & right);
        CxMessageLoop & operator =(const CxMessageLoop & right);
    };
}